package io.github.codelifeleno.jdbrelease.execute;

import cn.hutool.core.io.resource.Resource;
import cn.hutool.core.util.ArrayUtil;
import io.github.codelifeleno.jdbrelease.config.Block;
import io.github.codelifeleno.jdbrelease.config.DBScriptConfig;
import io.github.codelifeleno.jdbrelease.execute.StringStream.NegativeRule;
import io.github.codelifeleno.jdbrelease.execute.StringStream.PositiveRule;
import io.github.codelifeleno.jdbrelease.execute.StringStream.RuleBasedStreamReader;
import io.github.codelifeleno.jdbrelease.execute.StringStream.RuleCombinators;
import io.github.codelifeleno.jdbrelease.model.DBScriptRunModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

/**
 * 脚本执行的服务对象 用于执行脚本以及相关脚本的执行
 */
public class DBScriptFileService {

    private static final Logger log = LoggerFactory.getLogger(DBScriptFileService.class);
    private final DataBaseService dbService;

    private final DBScriptConfig dbScriptConfig;

    public DBScriptFileService(DataBaseService dbService, DBScriptConfig dbScriptConfig) {
        this.dbService = dbService;
        this.dbScriptConfig = dbScriptConfig;
    }

    public DataBaseService getDbService() {
        return dbService;
    }

    /**
     * 执行脚本过程，
     *
     * @param scriptFile 脚本文件 按照流式处理
     * @param runningDir 文件目录
     * @return 结果实例
     */
    public DBScriptRunModel runScript(Resource scriptFile, File runningDir) {
        List<NegativeRule> negativeRules = new ArrayList<>();
        List<PositiveRule> positiveRules = new ArrayList<>();
        String simple = dbScriptConfig.getSeparator().getSimple();
        String eof = dbScriptConfig.getSeparator().getEof();
        List<Block> blocks = dbScriptConfig.getComments().getBlocks();
        List<String> prefixes = dbScriptConfig.getComments().getSingleLine().getPrefixes();
        for (Block block : blocks) {
            NegativeRule rule = ((buffer, currentChar) -> buffer.toString().trim().startsWith(block.getStart()) && buffer.toString().trim().endsWith(block.getEnd()));
            negativeRules.add(rule);
        }
        for (String prefix : prefixes) {
            negativeRules.add(((buffer, currentChar) -> buffer.toString().trim().startsWith(prefix) && buffer.toString().endsWith("\\\\n")));
        }
        positiveRules.add((buffer, currentChar) -> buffer.toString().trim().startsWith(simple));
        positiveRules.add((buffer, currentChar) -> buffer.toString().trim().contains(eof));

        try (Reader reader = new BufferedReader(new InputStreamReader(scriptFile.getStream(), Charset.defaultCharset()));
             RuleBasedStreamReader streamReader = new RuleBasedStreamReader(reader, RuleCombinators.or(ArrayUtil.toArray(positiveRules, PositiveRule.class)), RuleCombinators.or(ArrayUtil.toArray(negativeRules, NegativeRule.class)));
        ) {

            AtomicReference<String> str = new AtomicReference<>("");
            AtomicInteger count = new AtomicInteger(0);
            AtomicBoolean errored = new AtomicBoolean(false);
            streamReader.stream((stream) -> {
                if (errored.get()) {
                    // 如果已经报错了，从下一句开始均不再向下执行，返回异常结论并执行回滚
                    return;
                }
                boolean b = this.dbService.execute(stream);
                errored.set(b);
                str.set(stream);
                count.getAndIncrement();
            });
            DBScriptRunModel model = new DBScriptRunModel(scriptFile, dbScriptConfig, str.get(), runningDir);
            model.setRunStatus(errored.get());
            return model;
        } catch (IOException e) {
            log.error("文件存在读写级异常，中断处理过程，回滚本次执行的全部文件：{}", e.getMessage(), e);
            throw new RuntimeException(e);
        }
    }

    public DBScriptConfig getDbScriptConfig() {
        return dbScriptConfig;
    }
}
